﻿using System;
using System.Reflection;
using élénkPróbája.native;

namespace élénkPróbája.attributes
{
    public class ValidatorAttributeFactory : ILitValidatorFactory, ILitParamValidatorFactory
    { 
        private readonly Func<Type, IValidator> instanceFactory;

        private readonly CacheFactory cache = new CacheFactory();

        /// <summary>
        /// Creates an instance of <see cref="ValidatorAttributeFactory"/>.
        /// </summary>
        public ValidatorAttributeFactory()
        {
            instanceFactory = null;
        }

        /// <summary>
        /// Creates an instance of <see cref="ValidatorAttributeFactory"/> with the supplied instance factory delegate
        /// used for creation of <see cref="IValidator"/> instances.
        /// </summary>
        /// <param name="instanceFactory">The <see cref="IValidator"/> instance factory delegate.</param>
        public ValidatorAttributeFactory(Func<Type, IValidator> instanceFactory)
        {
            this.instanceFactory = instanceFactory;
        }

        /// <summary>
        /// Gets a validator for the appropriate type.
        /// </summary>
        public IValidator<T> GetValidator<T>()
        {
            return (IValidator<T>)GetValidator(typeof(T));
        }

        /// <summary>
        /// Gets a validator for the appropriate type.
        /// </summary>
        /// <returns>Created <see cref="IValidator"/> instance; <see langword="null"/> if a validator cannot be
        /// created.</returns>
        public virtual IValidator GetValidator(Type type)
        {
            if (type == null)
            {
                return null;
            }

            var attribute = type.GetValidatorAttribute();

            return GetValidator(attribute);
        }

        /// <summary>
        /// Gets a validator for <paramref name="parameterInfo"/>.
        /// </summary>
        /// <param name="parameterInfo">The <see cref="ParameterInfo"/> instance to get a validator for.</param>
        /// <returns>Created <see cref="IValidator"/> instance; <see langword="null"/> if a validator cannot be
        /// created.</returns>
        public virtual IValidator GetValidator(ParameterInfo parameterInfo)
        {
            if (parameterInfo == null)
            {
                return null;
            }

            var attribute = parameterInfo.GetValidatorAttribute();

            return GetValidator(attribute);
        }

        private IValidator GetValidator(ValidatorAttribute attribute)
        {
            if (attribute?.ValidatorType == null)
            {
                return null;
            }

            var validator = instanceFactory == null
                ? cache.GetOrCreateInstance(attribute.ValidatorType)
                : cache.GetOrCreateInstance(attribute.ValidatorType, instanceFactory);

            return validator as IValidator;
        }
    }
}